package yunjiao.javatutorials.guava.concurrent.interrupt;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * 容错性资源管理器
 *
 * @author yangyunjiao
 */
public class FaultTolerantResourceManager {
    private final Lock lock = new ReentrantLock();
    private final InterruptAwareCondition resourceCondition;
    private final int maxResources;
    private int availableResources;
    private final AtomicInteger interruptedAcquisitions = new AtomicInteger(0);

    public FaultTolerantResourceManager(int maxResources) {
        this.maxResources = maxResources;
        this.availableResources = maxResources;
        this.resourceCondition = new InterruptAwareCondition(lock.newCondition());
    }

    public Resource acquire(long timeout, TimeUnit unit)
            throws InterruptedException, TimeoutException {
        lock.lock();
        try {
            long nanosTimeout = unit.toNanos(timeout);
            long deadline = System.nanoTime() + nanosTimeout;

            while (availableResources == 0) {
                if (nanosTimeout <= 0L) {
                    throw new TimeoutException("获取资源超时");
                }

                try {
                    nanosTimeout = resourceCondition.awaitNanos(nanosTimeout);
                } catch (InterruptedException e) {
                    interruptedAcquisitions.incrementAndGet();
                    System.out.println("资源获取被中断，可用资源: " + availableResources);

                    // 检查是否可以在中断前获取资源
                    if (availableResources > 0) {
                        availableResources--;
                        System.out.println("在中断前成功获取资源");
                        return new ResourceImpl();
                    }

                    throw e;
                }

                nanosTimeout = deadline - System.nanoTime();
            }

            availableResources--;
            System.out.println("资源获取成功，剩余资源: " + availableResources);
            return new ResourceImpl();

        } finally {
            lock.unlock();
        }
    }

    public Resource acquireUninterruptibly() {
        lock.lock();
        try {
            while (availableResources == 0) {
                resourceCondition.awaitUninterruptibly();

                if (resourceCondition.wasInterrupted()) {
                    System.out.println("不可中断等待期间检测到中断，但继续等待资源");
                    resourceCondition.clearInterruptFlag();
                }
            }

            availableResources--;
            System.out.println("资源获取成功(不可中断)，剩余资源: " + availableResources);
            return new ResourceImpl();

        } finally {
            lock.unlock();
        }
    }

    public void release(Resource resource) {
        lock.lock();
        try {
            availableResources++;
            resourceCondition.signal();
            System.out.println("资源已释放，可用资源: " + availableResources);
        } finally {
            lock.unlock();
        }
    }

    public void printResourceStatistics() {
        System.out.println("=== 资源管理器统计 ===");
        System.out.println("总资源数: " + maxResources);
        System.out.println("当前可用资源: " + availableResources);
        System.out.println("被中断的获取次数: " + interruptedAcquisitions.get());
        resourceCondition.printInterruptStatistics();
    }

    private class ResourceImpl implements Resource {
        private volatile boolean released = false;

        @Override
        public void use() {
            if (released) {
                throw new IllegalStateException("资源已释放");
            }
            System.out.println("使用资源...");
        }

        @Override
        public void release() {
            if (!released) {
                released = true;
                FaultTolerantResourceManager.this.release(this);
            }
        }
    }

    public static void main(String[] args) throws Exception {
        FaultTolerantResourceManager manager = new FaultTolerantResourceManager(2);

        // 测试正常获取和释放
        testNormalAcquisition(manager);

        // 测试中断处理
        testInterruptedAcquisition(manager);

        // 打印统计信息
        manager.printResourceStatistics();
    }

    private static void testNormalAcquisition(FaultTolerantResourceManager manager)
            throws Exception {
        System.out.println("=== 测试正常资源获取 ===");

        Resource res1 = manager.acquire(1, TimeUnit.SECONDS);
        Resource res2 = manager.acquire(1, TimeUnit.SECONDS);

        new Thread(() -> {
            try {
                Thread.sleep(500);
                res1.release();
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        }).start();

        // 这个应该等待直到第一个资源被释放
        Resource res3 = manager.acquire(2, TimeUnit.SECONDS);
        res2.release();
        res3.release();
    }

    private static void testInterruptedAcquisition(FaultTolerantResourceManager manager)
            throws InterruptedException, TimeoutException {
        System.out.println("=== 测试中断资源获取 ===");

        // 占用所有资源
        Resource res1 = manager.acquire(1, TimeUnit.SECONDS);
        Resource res2 = manager.acquire(1, TimeUnit.SECONDS);

        Thread acquisitionThread = new Thread(() -> {
            try {
                System.out.println("尝试获取资源(应该被阻塞)...");
                Resource res3 = manager.acquire(5, TimeUnit.SECONDS);
                res3.release();
            } catch (Exception e) {
                System.out.println("资源获取线程异常: " + e.getClass().getSimpleName());
            }
        });

        acquisitionThread.start();

        // 等待线程进入等待状态
        Thread.sleep(100);

        // 中断获取线程
        System.out.println("中断资源获取线程");
        acquisitionThread.interrupt();
        acquisitionThread.join();

        // 释放资源
        res1.release();
        res2.release();

        // 测试不可中断获取
        System.out.println("测试不可中断获取...");
        Thread uninterruptibleThread = new Thread(() -> {
            Resource res = manager.acquireUninterruptibly();
            System.out.println("不可中断获取成功");
            res.release();
        });

        uninterruptibleThread.start();
        Thread.sleep(100);
        uninterruptibleThread.interrupt();
        uninterruptibleThread.join();
    }

}
